package MyGui;

import java.util.Vector;


import Element.*;
import Element.Geom.*;
import DistSys.*;
/**
 * Title:        iPAD
 * Description:
 * Copyright:    Copyright (c) 2002
 * Company:      ABB Inc.
 * @author Fangxing Li
 * @version 1.0
 */

 /**
  * This class defines the internal rounding queue for redo/undo.
  * Indices ( iFirst, iCurr, iLast ) are baded on sliding window approach.
  */

public class EditActionQueue {
  public static final int NUM_ACT = 50;   //max steps of redo/undo
  public static final int MAX_SLIDING_WIND_SIZE = 1000;

  private EditAction[]  aEditAction;
  private int           iCurr;  //range=[iFirst -1, iLast] after init
  private int           iLast;
  private int           iFirst;

  public EditActionQueue() {
      aEditAction = new EditAction[ NUM_ACT ] ;
      reset();
  }

  public void reset(){
      iFirst = iLast = iCurr = -1;
  }

  private void push( EditAction ea ){
      if( iFirst == -1 ){ iFirst = 0; } //only executed once
      iCurr ++;
      iLast = iCurr;
      if( (iLast - iFirst) >= NUM_ACT ) { iFirst ++; }
      aEditAction[ iLast % NUM_ACT ] = ea;

      if( iLast > MAX_SLIDING_WIND_SIZE ){ // re-wind sliding window
          iFirst %= NUM_ACT;
          iCurr  %= NUM_ACT;
          iLast  %= NUM_ACT;
      }

      //System.out.println(iFirst + ", " + iCurr + ", " + iLast);
  }

  public void push( int id, Vector v){
      push( new EditAction( id, v) );
  }

  public void push( int id, AbsElem elem ){
      push( new EditAction(id, elem ) );
  }


  public boolean canUndo(){
      if( iFirst == -1) { return false; } //init state
      return ( (iCurr >= iFirst) ) ? true : false;
  }

  public void undo(){
      //if( iCurr < iFirst ) { return; }
      EditAction ea = aEditAction[ iCurr % NUM_ACT ];
      iCurr -- ;
      //System.out.println(iFirst + ", " + iCurr + ", " + iLast);
      AbsElem elem;
      ElemForUndoRedo elem2;
      if( ea.getInvolvedElemList().size()==0) { return; } //do nothing

      boolean bOnScreen = true; //No hurt even if an elem is not on screen
      boolean bSelected = false;
      int uid;
      Vector vInvolved = ea.getInvolvedElemList();
      switch ( ea.getActionId() ){
          case EditAction.ADD :
              Sys.deleteElemList( vInvolved );
              break;
          case EditAction.SMART_ADD:
          //SMART_ADD = ADD, MOVE
              //undo MOVE
              undoMove( (ElemForUndoRedo ) vInvolved.get(1) );
              //undo ADD
              Sys.deleteElem( (AbsElem) vInvolved.get(0) );
              break;
          case EditAction.DELETE:
              Sys.addElemInVector( vInvolved, bOnScreen, bSelected);
              break;
          case EditAction.SMART_DELETE:
          //SMART_DELETE = DELETE, MOVE; must process MOVE first
              //MOVE
              undoMove( (ElemForUndoRedo) vInvolved.get(1) );
              //DELETE
              Sys.addElem( (AbsElem) vInvolved.get(0), bOnScreen, bSelected);
              break;
          case EditAction.TOGGLE:
              Sys.toggleElemList( vInvolved );
              break;
          case EditAction.MOVE://same as undo
              for(int i=0; i< vInvolved.size(); i++){
                  undoMove((ElemForUndoRedo ) vInvolved.get(i) );
              }
              break;
          case EditAction.INSERT_NODE:
              //INSERT_NODE = DELETE, ADD, ADD
              Sys.addElem( (AbsElem) vInvolved.get(0), bOnScreen, bSelected );
              Sys.deleteElem( (AbsElem) vInvolved.get(1) );
              Sys.deleteElem( (AbsElem) vInvolved.get(2) );
              break;
          case EditAction.COMBINE_LINES:
              //COMBINE_LINES = DELETE (n-1) + ADD newLine;
              elem = (AbsElem) vInvolved.lastElement();
              Sys.deleteElem( elem );

              vInvolved.remove( elem );
              Sys.addElemInVector( vInvolved, bOnScreen, bSelected);
              vInvolved.add( elem );
              break;
          default:
              break;
      }
  }//end of undo();

  public boolean canRedo(){
      if( iFirst == -1) { return false; } //init state
      return (iCurr < iLast ) ? true : false;
  }

  public void redo(){
      //if( iCurr >= iLast ) { return; }
      iCurr ++;
      EditAction ea = aEditAction[ iCurr % NUM_ACT ];
      //System.out.println(iFirst + ", " + iCurr + ", " + iLast);
      AbsElem elem, elem2;
      if( ea.getInvolvedElemList().size()==0) { return; } //do nothing

      boolean bOnScreen = true; //No hurt even if an elem is not on screen
      boolean bSelected = false;
      int uid;
      Vector vInvolved = ea.getInvolvedElemList();
      switch ( ea.getActionId() ){
          case EditAction.ADD :
              Sys.addElemInVector( vInvolved, bOnScreen, bSelected);
              break;
          case EditAction.SMART_ADD:
          //SMART_ADD = ADD, MOVE
              //redo ADD
              Sys.addElem( (AbsElem)vInvolved.get(0), bOnScreen, bSelected);
              //redo MOVE
              redoMove( (ElemForUndoRedo) vInvolved.get(1) );
              break;
          case EditAction.DELETE:
              Sys.deleteElemList( vInvolved );
              break;
          case EditAction.SMART_DELETE:
          //SMART_DELETE = DELETE, MOVE; must process MOVE first
              //redo MOVE
              redoMove( (ElemForUndoRedo) vInvolved.get(1) );
              //redo DELETE
              Sys.deleteElem( (AbsElem) vInvolved.get(0));
              break;
          case EditAction.TOGGLE:
              Sys.toggleElemList( vInvolved );
              break;
          case EditAction.MOVE: //same as undo
              for(int i=0; i< vInvolved.size(); i++){
                  redoMove( (ElemForUndoRedo) vInvolved.get(i));
              }
              break;
          case EditAction.INSERT_NODE:
              //INSERT_NODE = DELETE, ADD, ADD
              Sys.deleteElem( (AbsElem) vInvolved.get(0) );
              Sys.addElem((AbsElem) vInvolved.get(1), bOnScreen, bSelected);
              Sys.addElem((AbsElem) vInvolved.get(2), bOnScreen, bSelected);
              break;
          case EditAction.COMBINE_LINES:
              //COMBINE_LINES == DELETE (n-1) + ADD newLine;
              elem = (AbsElem) vInvolved.lastElement();
              Sys.addElem( elem, bOnScreen, bSelected);

              vInvolved.remove( elem );
              Sys.deleteElemList( vInvolved );
              vInvolved.add( elem );
              break;
          default:
              break;
      }
  }//end of redo();

  private void undoMove( ElemForUndoRedo efur ){
      AbsElem elem = (AbsElem) efur.realElem;
      efur.swapNodeCoord( elem);
      Sys.clearConn( elem );
      Sys.updateConn( elem );
  }

  private void redoMove( ElemForUndoRedo efur ){
      undoMove( efur);
  }
}

class EditAction{
    public static final int ADD     = 0; //Also for COPY
    public static final int DELETE  = 1;
    public static final int TOGGLE  = 2;
    public static final int MOVE    = 3;  //Also for ROTATE
    public static final int INSERT_NODE   =4; //DEL, ADD, ADD
    public static final int COMBINE_LINES =5; //DEL * (n-1), ADD
    public static final int SMART_DELETE  =6; //DEL, MOVE
    public static final int SMART_ADD     =7; //ADD, MOVE

    int iActionId;
    Vector vInvolvedElemList = new Vector(100,100);

    public EditAction(){
    }

    public EditAction( int id, Vector v){
        iActionId = id;
        vInvolvedElemList.removeAllElements();
        vInvolvedElemList.addAll( v );
    }

    public EditAction( int id, AbsElem elem){
        iActionId = id;
        vInvolvedElemList.removeAllElements();
        vInvolvedElemList.add( elem );
    }

    public EditAction( int id, ElemForUndoRedo elem){
        iActionId = id;
        vInvolvedElemList.removeAllElements();
        vInvolvedElemList.add( elem );
    }

    public int getActionId(){ return this.iActionId; }
    public Vector getInvolvedElemList() { return this.vInvolvedElemList; }
}

class ElemForUndoRedo{
    AbsElem realElem;
    int x1Stored, y1Stored, x2Stored, y2Stored;

    public ElemForUndoRedo(AbsElem elem){
        realElem = elem;
        x1Stored = elem.getElemGeomObj().x1Stored;
        y1Stored = elem.getElemGeomObj().y1Stored;
        x2Stored = elem.getElemGeomObj().x2Stored;
        y2Stored = elem.getElemGeomObj().y2Stored;
    }

    public void swapNodeCoord(AbsElem e1){
        AbsElemGeom g1 = e1.getElemGeomObj();
        int x1Tmp, y1Tmp, x2Tmp, y2Tmp;
        x1Tmp = g1.x1Stored;
        y1Tmp = g1.y1Stored;
        x2Tmp = g1.x2Stored;
        y2Tmp = g1.y2Stored;
        g1.setNode1FromStored( this.x1Stored, this.y1Stored );
        g1.setNode2FromStored( this.x2Stored, this.y2Stored );
        this.x1Stored = x1Tmp;
        this.y1Stored = y1Tmp;
        this.x2Stored = x2Tmp;
        this.y2Stored = y2Tmp;
        if( g1 instanceof ThreeNodeElemGeom ){
            //g2 must be the same type; see beginning
            //since screen (x,y) already set in the above steps
            ((ThreeNodeElemGeom)g1).findNode3BasedonScreen();
            //((ThreeNodeElemGeom)g2).findNode3BasedonScreen();
        }
        if( e1 instanceof Line && e1.getAutoFlag(AbsElem.DATA_LENGTH) ){
            ((Line)e1).setLengthToAutoValue();
        }
        g1.calcFrame();
        //g2.calcFrame();
    }//end of swapNodeCoord( );

    /**
     * Create a list of EFUR for all elem in Vector vSrc
     */
    public static Vector createEfurForVector(Vector vSrc){
        AbsElem elem;
        Vector vRtn =  new Vector( vSrc.size() );
        for(int i=0; i< vSrc.size(); i++){
            elem = (AbsElem) vSrc.get(i);
            vRtn.add( new ElemForUndoRedo(elem) );
        }//end of for loop
        return vRtn;
    }//end of createEfurForVector();
}